// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

#include <fstream>
#include <stack>

#include "Deveco/CjpmProject.h"

using namespace CangjieMigrator;
using namespace CangjieMigrator::Deveco;

static std::vector<Path> EnumerateFilesWithoutCjRes(const Path& root)
{
    if (!exists(root)) {
        return {};
    }

    if (!is_directory(root)) {
        return {root};
    }

    std::vector<Path> files;
    std::stack<std::pair<Path, int>> toVisit; // pair<Path, Depth>
    toVisit.emplace(root, 0); // Depth 0 = root level
    while (!toVisit.empty()) {
        auto [dir, depth] = toVisit.top();
        toVisit.pop();

        for (const auto &entry :
             FileSystem::directory_iterator(dir,
                 FileSystem::directory_options::skip_permission_denied)) {
            if (FileSystem::is_regular_file(entry.path())) { // Add regular files
                files.push_back(entry.path());
                continue;
            }

            if (!FileSystem::is_directory(entry.path())) { // Skip non-directory entries (e.g., symlinks)
                continue;
            }

            if (is_symlink(entry.symlink_status())) {
                continue;
            }

            const auto name = entry.path().filename();
            if (name == "cj_res" || name == "oh_modules") {
                continue;
            }

            // Descend into subdirectory, increment depth
            toVisit.emplace(entry.path(), depth + 1);
        }
    }

    return files;
}

CjpmProject::CjpmProject(std::string name, Path path)
    : name(std::move(name)), path(std::move(path)), runParameter(CjpmRunParameter::EMPTY)
{}

bool CjpmProject::HasParameter() const
{
    return runParameter.IsValid();
}

std::string CjpmProject::ToString() const
{
    return name + " " + path.string();
}

std::string CjpmProject::ReadProjectName(const Path &projectToml)
{
    std::ifstream file(projectToml);
    if (!file) {
        return "";
    }

    std::string line;
    bool inside_package = false;
    while (std::getline(file, line)) {
        line.erase(0, line.find_first_not_of(" \t"));
        line.erase(line.find_last_not_of(" \t") + 1);

        if (line == "[package]") {
            inside_package = true;
            continue;
        }

        if (!inside_package) {
            continue;
        }

        if (line.size() < sizeof("name=\"a\"") - 1) {
            continue;
        }

        if (line.find("name") != 0) {
            continue;
        }

        const auto fifthChar = line[4];
        if (fifthChar != '=' && fifthChar != ' ' && fifthChar != '\t') {
            continue;
        }

        const auto equal = line.find('=');
        if (equal == std::string::npos) {
            continue;
        }

        const auto leftQuotation = line.find_first_of('\"');
        if (leftQuotation == std::string::npos) {
            continue;
        }

        const auto rightQuotation = line.find_last_of('\"');
        if (rightQuotation == std::string::npos) {
            continue;
        }

        if (rightQuotation == leftQuotation || rightQuotation - leftQuotation == 1) {
            continue;
        }

        return line.substr(leftQuotation + 1, rightQuotation - (leftQuotation + 1));
    }

    return "";
}

std::vector<CjpmProject> CjpmProject::GetAllFrom(const Path &directory)
{
    auto projects = std::vector<CjpmProject>();
    for (const auto &file : EnumerateFilesWithoutCjRes(directory)) {
        if (file.filename() == "cjpm.toml") {
            auto project = file.parent_path();
            auto name = ReadProjectName(file);
            if (name.empty()) {
                continue;
            }
            projects.emplace_back(std::move(name), std::move(project));
        }
    }
    return projects;
}
